#include "Board.h"

Board::Board(): cells(0), boardWidth(0), boardHeight(0)
{
}


Board::~Board()
{
	Clear();
}

unsigned int Board::CellCount() const
{
	return boardWidth * boardHeight;
}

void Board::Clear()
{
	if (cells)
	{
		delete[] cells;

		cells = 0;
	}
}


void Board::Resize(unsigned int width, unsigned int height)
{
	Clear();

	boardWidth = width;
	boardHeight = height;

	cells = new Cell[CellCount()];

	for(unsigned int y=0; y<height; y++)
	{
		for(unsigned int x=0; x<width; x++)
		{
			Cell& cell = CellAt(x, y);

			cell.SetPosition(x, y);
		}
	}
}


Cell& Board::CellAt(GameObject* object) const
{
	std::map<GameObject*, Cell*>::const_iterator it = objectmap.find(object);

	if (it == objectmap.end())
		throw "Board: Object not found on board";

	Cell* cell = (*it).second;

	return *cell;
}

Cell& Board::CellAt(unsigned int cellX, unsigned int cellY) const
{
	if (!IsInsideBoard(cellX, cellY))
		throw "Board: Cell position outside of board";

	return cells[ Index(cellX, cellY) ];
}

void Board::Update(GameObject* object)
{
	if (IsInsideBoard(object))
	{
		Cell& cell = CellAt(object);

		cell.Remove(object);

		objectmap.erase(object);
	}

	unsigned int objectCellX = object->CellX();
	unsigned int objectCellY = object->CellY();

	if (IsInsideBoard(objectCellX, objectCellY))
	{
		Cell& cell = CellAt(objectCellX, objectCellY);

		cell.Add(object);
		objectmap[object] = &cell;
	}
}


bool Board::IsInsideBoard(GameObject* object) const
{
	return objectmap.find(object) != objectmap.end();
}


bool Board::IsInsideBoard(unsigned int cellX, unsigned int cellY) const
{
	return cellX < boardWidth && cellY < boardHeight;
}


unsigned int Board::Index(unsigned int cellX, unsigned int cellY) const
{
	return cellX + cellY * boardWidth;
}


Cell* Board::ObjectNeighbourAt(GameObject* object, const Direction& direction) const
{
	if (!IsInsideBoard(object))
		return false;

	Cell& cell = CellAt(object);

	return NeighbourAt(cell, direction);
}


Cell* Board::NeighbourAt(Cell& cell, const Direction& direction) const
{
	unsigned int cellX = cell.CellX();
	unsigned int cellY = cell.CellY();


	switch (direction.Type())
	{
		case Direction::NORTH:
			cellY--;
		break;
		case Direction::SOUTH:
			cellY++;
		break;
		case Direction::EAST:
			cellX++;
		break;
		case Direction::WEST:
			cellX--;
		break;
	}

	if (IsInsideBoard(cellX, cellY))
		return &CellAt(cellX, cellY);
	else
		return 0;
}

void Board::Print()
{
	for(unsigned int y=0; y<boardHeight; y++)
	{
		for(unsigned int x=0; x<boardWidth; x++)
		{
			printf("%s", CellAt(x, y).ToString().c_str());
		}

		printf("\n");
	}
}
